#ifndef __CLIENT_SESSION_HPP__
#define __CLIENT_SESSION_HPP__

#include "../luoo/luoo_packet.hpp"
#include "../luoo/luoo_message_queue.hpp"

#include <boost/asio.hpp>

class client_session
{
public:
	client_session(boost::asio::io_service& io_service)
		: io_service_(io_service)
		, socket_(io_service)
		, packet_header_(new luoo::packet_header(0, 0))
	{}
public:
	void start(boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
	{
		boost::asio::async_connect(socket_, endpoint_iterator, 
			boost::bind(&client_session::handle_connect, 
			this, boost::asio::placeholders::error)
			);
	}
	void write(const void* data_ptr, unsigned int data_len)
	{
		io_service_.post(boost::bind(&client_session::do_write, this, data_ptr, data_len));
	}

	void close()
	{
		io_service_.post(boost::bind(&client_session::do_close, this));
	}
private:
	void handle_connect(const boost::system::error_code& error)
	{
		if (!error)
		{
			std::cout << "connect sucessed!" << std::endl;
			boost::asio::async_read(socket_,
				boost::asio::buffer(packet_header_, sizeof(luoo::packet_header)),
				boost::bind(&client_session::handle_read_header, this,
				boost::asio::placeholders::error));
		}
		else
		{
			std::cout << "handle_connect failed!" << error.message() << std::endl;
		}
	}

	void handle_read_header(const boost::system::error_code& error)
	{
		if (!error)
		{
			boost::asio::async_read(socket_,
				boost::asio::buffer(data_, packet_header_->get_body_length()),
				boost::bind(&client_session::handle_read_body, this,
				boost::asio::placeholders::error));
		}
		else
		{
			std::cout << "handle_read_header failed!" << error.message() << std::endl;
			do_close();
		}
	}

	void handle_read_body(const boost::system::error_code& error)
	{
		if (!error)
		{
			unsigned char* data_ptr = new unsigned char[packet_header_->get_packet_length()];
			memcpy(data_ptr, packet_header_, sizeof(luoo::packet_header));
			memcpy(data_ptr + sizeof(luoo::packet_header), data_, packet_header_->get_body_length());

			luoo::packet_header* header = reinterpret_cast<luoo::packet_header*>(data_ptr);

			if (!header->chaeck_packet_signature())
			{
				std::cout << "chaeck_packet_signature failed!" << std::endl;
			}
			if (!header->chaeck_packet_version())
			{
				std::cout << "chaeck_packet_version failed!" << std::endl;
			}
			if (header->decode())
			{
				std::cout << "decode failed!" << std::endl;
			}

			delete[] data_ptr;

			boost::asio::async_read(socket_,
				boost::asio::buffer(packet_header_, sizeof(luoo::packet_header)),
				boost::bind(&client_session::handle_read_header, this,
				boost::asio::placeholders::error));
		}
		else
		{
			std::cout << "handle_read_body failed!" << error.message() << std::endl;
			do_close();
		}
	}
	
	void do_write(const void* data_ptr, unsigned int data_len)
	{
		bool write_in_progress = !send_queue_.empty();
		luoo::message_queue::message msg;
		msg.data_ptr = new luoo::uint8[data_len];
		msg.data_len = data_len;
		memcpy(msg.data_ptr, data_ptr, data_len);
		send_queue_.push(msg);
		if (!write_in_progress)
		{
			boost::asio::async_write(socket_,
				boost::asio::buffer(send_queue_.front().data_ptr,
				send_queue_.front().data_len),
				boost::bind(&client_session::handle_write, this,
				boost::asio::placeholders::error));
		}
	}

	void handle_write(const boost::system::error_code& error)
	{
		if (!error)
		{
			delete send_queue_.pop();
			if (!send_queue_.empty())
			{
				boost::asio::async_write(socket_,
					boost::asio::buffer(send_queue_.front().data_ptr,
					send_queue_.front().data_len),
					boost::bind(&client_session::handle_write, this,
					boost::asio::placeholders::error));
			}
		}
		else
		{
			std::cout << "handle_write failed!" << error.message() << std::endl;
			do_close();
		}
	}

	void do_close()
	{
		socket_.close();
	}

private:
	boost::asio::io_service& io_service_;
	boost::asio::ip::tcp::socket socket_;
	luoo::packet_header* packet_header_;
	luoo::uint8 data_[1024];
	luoo::message_queue send_queue_;
};

#endif//__CLIENT_SESSION_HPP__